package datastore

import (
	"context"
	"errors"
	"strconv"
	"testing"

	"github.com/stackrox/rox/central/imageintegration/store"
	mockIIStore "github.com/stackrox/rox/central/imageintegration/store/mocks"
	"github.com/stackrox/rox/generated/storage"
	"github.com/stackrox/rox/pkg/buildinfo"
	"github.com/stackrox/rox/pkg/env"
	"github.com/stackrox/rox/pkg/features"
	"github.com/stackrox/rox/pkg/openshift"
	scannerTypes "github.com/stackrox/rox/pkg/scanners/types"
	"github.com/stackrox/rox/pkg/testutils"
	"github.com/stretchr/testify/assert"
	"go.uber.org/mock/gomock"
)

func TestDeleteAutogeneratedRegistries(t *testing.T) {
	ctx := context.Background()

	iis := []*storage.ImageIntegration{
		{Id: "NoAutogen-GlobalPull", Autogenerated: false, Source: &storage.ImageIntegration_Source{Namespace: openshift.GlobalPullSecretNamespace, ImagePullSecretName: openshift.GlobalPullSecretName}},
		{Id: "NoAutogen-NoSource", Autogenerated: false, Source: nil},
		{Id: "NoAutogen-Source", Autogenerated: false, Source: &storage.ImageIntegration_Source{}},
		{Id: "Autogen-GlobalPull", Autogenerated: true, Source: &storage.ImageIntegration_Source{Namespace: openshift.GlobalPullSecretNamespace, ImagePullSecretName: openshift.GlobalPullSecretName}},
		{Id: "Autogen-NoSource", Autogenerated: true, Source: nil},
		{Id: "Autogen-Source", Autogenerated: true, Source: &storage.ImageIntegration_Source{}},
	}

	testCases := []struct {
		name                     string
		expectedDeletes          []string
		autogenEnabled           bool
		sourcedAutogenEnabled    bool
		globalPullAutogenEnabled bool
	}{
		{
			"delete all autogen integrations when all autogen features disabled",
			[]string{"Autogen-GlobalPull", "Autogen-NoSource", "Autogen-Source"},
			false, false, false,
		},
		{
			"delete all autogen integrations when autogen disabled, sourced enabled, global pull disabled",
			[]string{"Autogen-GlobalPull", "Autogen-NoSource", "Autogen-Source"},
			false, true, false,
		},
		{
			"delete all autogen integrations when autogen disabled, sourced disabled, global pull enabled",
			[]string{"Autogen-GlobalPull", "Autogen-NoSource", "Autogen-Source"},
			false, false, true,
		},
		{
			"delete all autogen integrations when autogen disabled and all other autogen features enabled",
			[]string{"Autogen-GlobalPull", "Autogen-NoSource", "Autogen-Source"},
			false, true, true,
		},
		{
			"delete only sourced autogen integrations when autogen enabled and all other autogen features disabled",
			[]string{"Autogen-GlobalPull", "Autogen-Source"},
			true, false, false,
		},
		{
			"delete only non global pull sourced integrations when autogen enabled, sourced autogen disabled, and global pull enabled",
			[]string{"Autogen-Source"},
			true, false, true,
		},
		{
			"delete no integrations when autogen enabled, sourced autogen enabled, and global pull disabled",
			[]string{},
			true, true, false,
		},
		{
			"delete no integrations when all autogen features enabled",
			[]string{},
			true, true, true,
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			testutils.MustUpdateFeature(t, features.SourcedAutogeneratedIntegrations, tc.sourcedAutogenEnabled)
			t.Setenv(env.AutogeneratedRegistriesDisabled.EnvVar(), strconv.FormatBool(!tc.autogenEnabled))
			t.Setenv(env.AutogenerateGlobalPullSecRegistries.EnvVar(), strconv.FormatBool(tc.globalPullAutogenEnabled))

			ctrl := gomock.NewController(t)
			s := mockIIStore.NewMockStore(ctrl)
			if len(tc.expectedDeletes) > 0 {
				s.EXPECT().PruneMany(ctx, tc.expectedDeletes)
			}

			deleteAutogeneratedRegistries(ctx, s, iis)
		})
	}
}

func TestSetupScannerV4Integration(t *testing.T) {
	ctx := context.Background()

	defID := store.DefaultScannerV4Integration.GetId()

	iis := []*storage.ImageIntegration{
		{Id: "ID-Clairify", Type: scannerTypes.Clairify},
		{Id: "ID-Other-ScannerV4", Type: scannerTypes.ScannerV4},
		store.DefaultScannerV4Integration,
	}

	t.Run("do nothing if no scanner v4 integrations", func(t *testing.T) {
		testutils.MustUpdateFeature(t, features.ScannerV4, false)
		s := mockIIStore.NewMockStore(gomock.NewController(t))

		setupScannerV4Integration(ctx, s, iis[:1])
	})

	t.Run("delete scanner v4 integrations if the feature is disabled", func(t *testing.T) {
		testutils.MustUpdateFeature(t, features.ScannerV4, false)
		s := mockIIStore.NewMockStore(gomock.NewController(t))

		s.EXPECT().PruneMany(ctx, []string{"ID-Other-ScannerV4", defID})
		setupScannerV4Integration(ctx, s, iis)
	})

	t.Run("delete non default scanner v4 integrations and upsert default integration if feature enabled", func(t *testing.T) {
		testutils.MustUpdateFeature(t, features.ScannerV4, true)
		s := mockIIStore.NewMockStore(gomock.NewController(t))

		s.EXPECT().Get(ctx, defID).Return(nil, false, nil)
		s.EXPECT().Upsert(ctx, store.DefaultScannerV4Integration).Return(nil)
		s.EXPECT().PruneMany(ctx, []string{"ID-Other-ScannerV4"})
		setupScannerV4Integration(ctx, s, iis)
	})

	t.Run("do not try upsert if error getting default integration", func(t *testing.T) {
		testutils.MustUpdateFeature(t, features.ScannerV4, true)
		s := mockIIStore.NewMockStore(gomock.NewController(t))

		s.EXPECT().Get(ctx, defID).Return(nil, false, errors.New("fake"))

		if buildinfo.ReleaseBuild {
			s.EXPECT().PruneMany(ctx, []string{"ID-Other-ScannerV4"})
			setupScannerV4Integration(ctx, s, iis)
		} else {
			assert.Panics(t, func() { setupScannerV4Integration(ctx, s, iis) })
		}
	})

	t.Run("do not try upsert if default integration exists", func(t *testing.T) {
		testutils.MustUpdateFeature(t, features.ScannerV4, true)
		s := mockIIStore.NewMockStore(gomock.NewController(t))

		s.EXPECT().Get(ctx, defID).Return(nil, true, nil)
		s.EXPECT().PruneMany(ctx, []string{"ID-Other-ScannerV4"})
		setupScannerV4Integration(ctx, s, iis)
	})
}
